package parser.domain;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import main.Main;

public class LL1Table {
	private HashMap<Symbol, HashMap<Symbol, ArrayList<Symbol>>> table;

	public LL1Table(Grammar grammar) {
		Set<Symbol> symbols = new HashSet<Symbol>();
		symbols.addAll(grammar.getNonTerminals());
		symbols.addAll(grammar.getTerminals());
		Set<TerminalSymbol> terminals = grammar.getTerminals();
		Set<Production> productions = grammar.getProductionRules();
		table = new HashMap<Symbol, HashMap<Symbol, ArrayList<Symbol>>>();
		First first = new First(grammar);
		Follow follow = new Follow(grammar, first);

		for (Symbol symbol : symbols) {
			HashMap<Symbol, ArrayList<Symbol>> pair = new HashMap<>();
			for (Symbol terminal : terminals) {
				ArrayList<Symbol> productionRhsList = new ArrayList<>();
				pair.put(terminal, productionRhsList);
			}
			table.put(symbol, pair);
		}
		for (Production production : productions) {
			for (TerminalSymbol terminal : terminals) {
				List<Symbol> alpha = production.getRhs();
				if (alpha.size() > 1
						|| !alpha.contains(new TerminalSymbol(Main.EPSILON))) {// first
																				// case
																				// from
																				// lecture
					if (first.getFirst(alpha).contains(terminal)) {
						addToTable(production, terminal, alpha);
					}
				} else {// second case from lecture
					if (follow.getFollow(production.getLhs())
							.contains(terminal)
							&& first.getFirst(alpha).contains(
									new TerminalSymbol(Main.EPSILON))) {
						addToTable(production, terminal, alpha);
					}
				}
			}
		}
		// productionRhsList.addAll(p.getRhs());
//		printStuff();
	}

	public void printStuff() {
		for (Symbol symbol : table.keySet()) {
			for (Symbol s : table.get(symbol).keySet()) {
				System.out.println(symbol + " " + s + " "
						+ table.get(symbol).get(s));
			}
		}
	}

	private void addToTable(Production production, TerminalSymbol terminal,
			List<Symbol> alpha) {
		if (table.get(production.getLhs()).get(terminal).size() != 0) {
			// ll1 conflict. GTFO
			System.out.println("LL1 conflict");
			System.exit(1);
		}
		table.get(production.getLhs()).get(terminal).addAll(alpha);
	}
	
	public List<Symbol> get(Symbol first, TerminalSymbol second){
		return table.get(first).get(second);
	}
}
